package com.cqndt.disaster.device.warn;

import com.cqndt.disaster.device.dao.TabAlarmInfoMapper;
import com.cqndt.disaster.device.dao.TabDeviceMapper;
import com.cqndt.disaster.device.dao.TabMonitorMapper;
import com.cqndt.disaster.device.dao.TabMsgRecordMapper;
import com.cqndt.disaster.device.dao.warn.WarnDlfSettingMapper;
import com.cqndt.disaster.device.dao.warn.WarnPlanMapper;
import com.cqndt.disaster.device.dao.warn.WarnQxSettingMapper;
import com.cqndt.disaster.device.domain.*;
import com.cqndt.disaster.device.message.SudasSmsUtil;
import com.cqndt.disaster.device.utils.DateUtil;
import com.cqndt.disaster.device.vo.TabMsgRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;

/**
 * 倾斜
 *
 * @Author yin_q
 * @Date 2019/9/10 10:41
 * @Email yin_qingqin@163.com
 **/
@Component
@Slf4j
public class QxWarn {

    @Autowired
    WarnQxSettingMapper warnQxSettingMapper;

    @Autowired
    WarnPlanMapper warnPlanMapper;

    @Autowired
    TabMonitorMapper tabMonitorMapper;

    @Autowired
    TabDeviceMapper tabDeviceMapper;

    @Autowired
    TabAlarmInfoMapper tabAlarmInfoMapper;

    @Autowired
    TabMsgRecordMapper tabMsgRecordMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Async("taskExecutor")
    @Scheduled(initialDelay = 10000, fixedDelay = 10000)
    public void executePlan(){

        log.info("倾斜->开始进行倾斜判断");

        // 取出待判断得告警信息
        List<WarnPlan> warnPlanList = warnPlanMapper.queryWarnPlan(2);

        for(WarnPlan warnPlan : warnPlanList){

            // 删除本次预警判断
            warnPlanMapper.deleteWarnPlanById(warnPlan.getId());

            /**
             * 预警逻辑
             * 1.取出对应设备得预警阈值信息，如无对应设备得预警信息则不进行后续判断
             * 2.分别判断对应阈值信息，看是否达到预警条件
             * 累积：本次接收到的值-初始值
             * 相邻：本次接收到的值-上次接收到的值
             * 3.满足阈值信息时，则进行短信人员筛选，并发送对应短信内容
             * 4.存储和推送对应预警信息，以及对应短信信息
             */

            /*
             * 第一步，判断是否存在预警信息
             */
            TabDevice tabDevice = tabDeviceMapper.getDevice(warnPlan.getSensorNo());

            if(tabDevice == null){
                log.info("倾斜->设备{}，暂无设备信息，不进行预警判断！", warnPlan.getSensorNo());
                continue;
            }

            List<WarnQxSetting> warnQxSettingList = warnQxSettingMapper.queryWarnQxSettingList(String.valueOf(tabDevice.getId()));

            if(warnQxSettingList == null || warnQxSettingList.isEmpty()){
                log.info("倾斜->设备{}，暂无阈值信息，不进行预警判断！", warnPlan.getSensorNo());
                continue;
            }

            /*
             * 第二步，判断是否满足阈值
             */
            WarnQxSetting setting = null;

            for(WarnQxSetting warnQxSetting : warnQxSettingList){
                // 取出对应标识信息进行判断
                if(warnPlan.getSign().equals(warnQxSetting.getSign())){
                    setting = warnQxSetting;
                    break;
                }
            }

            if(setting == null){
                log.info("倾斜->设备{},未发现标识【{}】的阈值信息，不进行预警判断", warnPlan.getSensorNo(), warnPlan.getSign());
                continue;
            }

            HashOperations<String, Object, Object> vo = stringRedisTemplate.opsForHash();
            Map<Object, Object> entries = vo.entries(warnPlan.getSensorNo()+"_"+ warnPlan.getSign());
            boolean hasXl = true;
            if(entries == null || entries.isEmpty()){
                // 如果取不到上一次值，则将相邻判断设置为false,不进行相邻判断
                hasXl = false;
            }

            // 进行相邻预警判断
            if(hasXl){
                // 上次值
                BigDecimal lastValue = BigDecimal.valueOf(Double.valueOf(entries.get("value").toString()));

                // |当次值绝对值-上次值绝对值|
                BigDecimal diff = (warnPlan.getValue().abs().subtract(lastValue.abs())).abs().setScale(2,BigDecimal.ROUND_HALF_UP) ;

                // 进行相邻告警等级判断
                Integer level = WarnLv(diff, setting, true);

                // 达到相邻预警条件
                if(level > 0){
                    log.info("倾斜->设备{}，触发相邻告警，告警等级-{}", warnPlan.getSensorNo(), level);
                    sendSms(tabDevice.getId(), tabDevice, setting, true, level, warnPlan.getValue(), lastValue, diff);
                }

            }

            // 进行累积预警判断
            // |当次值绝对值-初始值绝对值|
            BigDecimal diff = (warnPlan.getValue().abs().subtract(setting.getInitialValue().abs())).abs().setScale(2,BigDecimal.ROUND_HALF_UP) ;

            // 进行累积告警等级判断
            Integer level = WarnLv(diff, setting, false);

            if(level > 0){
                log.info("倾斜->设备{}，触发累积告警，告警等级-{}", warnPlan.getSensorNo(), level);
                sendSms(tabDevice.getId(), tabDevice, setting, false, level, warnPlan.getValue(), null, diff);
            }


            // 存储本次值，代表本次值为最后一次判断值，用于后续值判断的上一次值
            vo.put(warnPlan.getSensorNo()+"_"+ warnPlan.getSign(), "time", DateUtil.dateToString(new Date()));
            vo.put(warnPlan.getSensorNo()+"_"+ warnPlan.getSign(), "value", warnPlan.getValue().toString());
            log.info("倾斜->设备{},阈值判断结束", warnPlan.getSensorNo());
        }
    }

    /**
     * 发送短信
     * @param sensorNo
     * @param warnQxSetting
     * @param hasXl
     * @param level
     * @param currentValue
     * @param value
     * @param result
     */
    public void sendSms(Integer sensorNo, TabDevice tabDevice, WarnQxSetting warnQxSetting, boolean hasXl, Integer level, BigDecimal currentValue, BigDecimal value, BigDecimal result){
        // 项目名称
        String projectName = tabMonitorMapper.getProjectNameByDeviceId(sensorNo);

        // 设备名称
        String deviceName = tabDevice == null ? "" : tabDevice.getDeviceName();

        // 告警颜色
        String color = level == 1 ? "蓝色" : (level == 2 ? "黄色" : (level == 3 ? "橙色" : (level == 4 ? "红色" : "")));

        /**
         *  拼接短信内容
         */
        String message = "";

        if(hasXl){
            /**
             * 相邻告警
             */
            // 上次监测值
            String lastValue = value.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 当前值
            String current = currentValue.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 相邻值
            String resultValue = result.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 阈值
            BigDecimal warn = level == 1 ? warnQxSetting.getXlAlarmBlue() : (level == 2 ? warnQxSetting.getXlAlarmYellow() : (level == 3 ? warnQxSetting.getXlAlarmOrange() : (level == 4 ? warnQxSetting.getXlAlarmRed() : BigDecimal.ZERO)));
            String warnValue = warn.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            message = smsXlMessage(projectName, deviceName, warnQxSetting.getSignName(), color, tabDevice.getDeviceNo(), lastValue , current, resultValue, warnValue);
        } else {
            /**
             * 累积告警
             */
            // 初始值
            String initValue = warnQxSetting.getInitialValue().setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 当前值
            String current = currentValue.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 累积值
            String resultValue = result.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            // 阈值
            BigDecimal warn = level == 1 ? warnQxSetting.getLjAlarmBlue() : (level == 2 ? warnQxSetting.getLjAlarmYellow() : (level == 3 ? warnQxSetting.getLjAlarmOrange() : (level == 4 ? warnQxSetting.getLjAlarmRed() : BigDecimal.ZERO)));
            String warnValue = warn.setScale(2,BigDecimal.ROUND_HALF_UP) + warnQxSetting.getUnit();
            message = smsLjMessage(projectName, deviceName, warnQxSetting.getSignName(), color, tabDevice.getDeviceNo(), initValue, current, resultValue, warnValue);
        }

        // 告警短信发送人员列表
        List<Map<String, Object>> personsList = tabAlarmInfoMapper.getAlarmPersons();

        // 去除重复短信发送
        Map<String, String> phoneMap = new HashMap<>();

        for (Map<String, Object> personInfo : personsList) {
            String phone = personInfo.get("phone").toString();
            String name = personInfo.get("name").toString();
            Integer personLevel = Integer.valueOf(personInfo.get("level").toString());
            Integer type = Integer.valueOf(personInfo.get("type").toString());
            if (type == 4 || type == 1) {
                // 反向判断，人员上的值是 4 红  3  橙  2 黄  1 蓝
                Integer lv = level == 1 ? 4 : (level == 2 ? 3 : (level == 3 ? 2 : 1));
                if (lv >= personLevel) {
                    phoneMap.put(phone, name);
                }
            }
        }

        /**
         * 发送短信
         */
        int count = 0;
        for(Map.Entry<String, String> entry : phoneMap.entrySet()){
            SudasSmsUtil.send(entry.getKey(), message);
            TabMsgRecord tabMsgRecord = new TabMsgRecord();
            tabMsgRecord.setSendPerson("yunnanAdmin");
            tabMsgRecord.setPhone(entry.getKey());
            tabMsgRecord.setSendState(1);
            tabMsgRecord.setContent(message);
            tabMsgRecord.setSendWay(1);
            tabMsgRecord.setSendTime(new Date());
            tabMsgRecordMapper.addTabMsgRecord(tabMsgRecord);
            count++;
        }

        log.info("倾斜->设备{},发送短信成功，本次发送短信数量{}", sensorNo, count);

        /**
         * 保存预警信息
         */
        int monitorId = tabMonitorMapper.selectIdByDeviceNo(sensorNo);
        insertAlarm(tabDevice.getDeviceNo(), new Date(), level, Double.valueOf(currentValue.toString()), monitorId, hasXl ? 2 : 1, message);
    }

    /**
     * 相邻短信内容信息
     * @param projectName  项目名称
     * @param deviceName  设备名称
     * @param signName  告警描述
     * @param color 告警等级
     * @param deviceNo 告警设备编号
     * @param lastValue 上次监测值
     * @param currentValue  本次监测值
     * @param xlValue  相邻告警值
     * @param warnValue  阈值
     * @return
     */
    public String smsXlMessage(String projectName, String deviceName, String signName, String color, String deviceNo, String lastValue, String currentValue, String xlValue, String warnValue){
        return MessageFormat.format("{0}监测:{1}设备,{2}触发{3}相邻告警,设备编号为:{4},上次监测值:{5},本次监测值:{6},相邻值:{7},阈值{8},请及时关注,监测时间:{9}",
                projectName,
                deviceName,
                signName,
                color,
                deviceNo,
                lastValue,
                currentValue,
                xlValue,
                warnValue,
                DateUtil.dateToString(new Date()));
    }

    /**
     * 累积告警短信内容
     * @param projectName  项目名称
     * @param deviceName  设备名称
     * @param signName  告警描述
     * @param color 告警等级
     * @param deviceNo 告警设备编号
     * @param initValue 上次监测值
     * @param currentValue  本次监测值
     * @param ljValue  累积值
     * @param warnValue  阈值
     * @return
     */
    public String smsLjMessage(String projectName, String deviceName, String signName, String color, String deviceNo, String initValue, String currentValue, String ljValue, String warnValue){
        return MessageFormat.format("{0}监测:{1}设备,{2}触发{3}累积告警,设备编号为:{4},初始监测值:{5},本次监测值:{6},累积值:{7},阈值:{8},请及时关注,监测时间:{9}",
                projectName,
                deviceName,
                signName,
                color,
                deviceNo,
                initValue,
                currentValue,
                ljValue,
                warnValue,
                DateUtil.dateToString(new Date()));
    }

    /**
     * 告警等级判断方法
     * @param diff  差值
     * @param warnQxSetting  阈值信息
     * @param hasXl 告警类型  true：相邻  false：累积
     * @return
     */
    public Integer WarnLv(BigDecimal diff, WarnQxSetting warnQxSetting, boolean hasXl){
        List<BigDecimal> warnList = new ArrayList<>();
        if(hasXl){
            /*
             * 相邻告警
             * 预警值存入顺序
             * 蓝、黄、橙、红
             */
            warnList.add(warnQxSetting.getXlAlarmBlue());
            warnList.add(warnQxSetting.getXlAlarmYellow());
            warnList.add(warnQxSetting.getXlAlarmOrange());
            warnList.add(warnQxSetting.getXlAlarmRed());
        } else {
            /*
             * 累积告警
             * 预警值存入顺序
             * 蓝、黄、橙、红
             */
            warnList.add(warnQxSetting.getLjAlarmBlue());
            warnList.add(warnQxSetting.getLjAlarmYellow());
            warnList.add(warnQxSetting.getLjAlarmOrange());
            warnList.add(warnQxSetting.getLjAlarmRed());
        }

        int level = 0;

        for(int i = 0; i < warnList.size(); i++){
            if(diff.compareTo(warnList.get(i)) > 0){
                // 当前值，比此等级要高，则进行下一级别判断
                level = i+1;
                continue;
            } else if(diff.compareTo(warnList.get(i)) == 0){
                // 当前值与等级相等，则停止循环
                level = i+1;
                break;
            }

            // 当前比蓝色阈值还低，则直接停止循环
            if(level == 0){
                level = 0;
                break;
            }
        }
        return level;
    }

    /**
     * 存入告警信息
     * @param sensorNo  设备编号  deviceNo
     * @param warnTime  告警时间
     * @param level  告警等级
     * @param value  告警值
     * @param monitorId  监测ID
     * @param type  告警类型
     * @param smsContent  告警短信内容
     */
    private void insertAlarm(String sensorNo, Date warnTime, int level, Double value, int monitorId, int type, String smsContent) {
        TabAlarmInfo tabAlarmInfo = new TabAlarmInfo();
        tabAlarmInfo.setSensorNo(sensorNo);
        tabAlarmInfo.setMonitoringId(monitorId);
        tabAlarmInfo.setTime(warnTime);
        tabAlarmInfo.setValue(value);
        tabAlarmInfo.setLevel(level == 1 ? 4 : (level == 2 ? 3 : (level == 3 ? 2 : 1)));
        tabAlarmInfo.setAlarmType(type);
        tabAlarmInfo.setStatus(1);
        tabAlarmInfo.setUploadSend(0);
        tabAlarmInfo.setSmsContent(smsContent);
        tabAlarmInfoMapper.insertSelective(tabAlarmInfo);
    }

}
